<!doctype html><html lang=zh-cn dir=ltr>
<head><meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="其实“I/O多路复用”这个坑爹翻译可能是这个概念在中文里面如此难理解的原因。所谓的I/O多路 复用在英文中其实叫 I/O multiplexing. 如果你搜索multiplexing啥意思,基本上都会出这个图:
于是大部分人都直接联想到&amp;quot;一根网线,多个sock复用&amp;quot; 这个概念,包括上面的几个回答, 其实不 管你用多进程还是I/O多路复用, 网线都只有一根好伐。多个Sock复用一根网线这个功能是在内核 +驱动层实现的.
重要的事情再说一遍: I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记 录跟踪每一个Sock(I/O流)的状态(对应空管塔里面的Fight progress strip槽)来同时管理多个I/O 流. 发明它的原因,是尽量多的提高服务器的吞吐能力。
是不是听起来好拗口,看个图就懂了.
在同一个线程里面, 通过拨开关的方式,来同时传输多个I/O流, (学过EE的人现在可以站出来义正 严辞说这个叫“时分复用”了)。
什么,你还没有搞懂 “一个请求到来了,nginx使用epoll接收请求的过程是怎样的”, 多看看这个 图就了解了。提醒下,ngnix会有很多链接进来, epoll会把他们都监视起来,然后像拨开关一样, 谁有数据就拨向谁,然后调用相应的代码处理。
 了解这个基本的概念以后,其他的就很好解释了。
select, poll, epoll 都是I/O多路复用的具体的实现,之所以有这三个鬼存在,其实是他们出现是有 先后顺序的。
I/O多路复用这个概念被提出来以后, select是第一个实现 (1983 左右在BSD里面实现的)。
select 被实现以后,很快就暴露出了很多问题。
 select 会修改传入的参数数组,这个对于一个需要调用很多次的函数,是非常不友好的。 select 如果任何一个sock(I/O stream)出现了数据,select 仅仅会返回,但是并不会告诉你是那 个sock上有数据,于是你只能自己一个一个的找,10几个sock可能还好,要是几万的sock每次 select 不是线程安全的,如果你把一个sock加入到select, 然后突然另外一个线程发现,尼玛,这 个sock不用,要收回。对不起,这个select 不支持的,如果你丧心病狂的竟然关掉这个sock, select的标准行为是。。呃。。不可预测的, 这个可是写在文档中的哦.   “If a file descriptor being monitored by select() is closed in another thread, the result is unspecified."><title>MultiplexingIO</title><link rel=canonical href=https://sdttttt.github.io/blog/multiplexingio/>
<link rel=stylesheet href=/scss/style.min.b80bf249ce4a22cf55e8d7340a0b37a2f2c10f54f3a9a49cb94b694a2eb0bbea.css><meta property="og:title" content="MultiplexingIO">
<meta property="og:description" content="其实“I/O多路复用”这个坑爹翻译可能是这个概念在中文里面如此难理解的原因。所谓的I/O多路 复用在英文中其实叫 I/O multiplexing. 如果你搜索multiplexing啥意思,基本上都会出这个图:
于是大部分人都直接联想到&amp;quot;一根网线,多个sock复用&amp;quot; 这个概念,包括上面的几个回答, 其实不 管你用多进程还是I/O多路复用, 网线都只有一根好伐。多个Sock复用一根网线这个功能是在内核 +驱动层实现的.
重要的事情再说一遍: I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记 录跟踪每一个Sock(I/O流)的状态(对应空管塔里面的Fight progress strip槽)来同时管理多个I/O 流. 发明它的原因,是尽量多的提高服务器的吞吐能力。
是不是听起来好拗口,看个图就懂了.
在同一个线程里面, 通过拨开关的方式,来同时传输多个I/O流, (学过EE的人现在可以站出来义正 严辞说这个叫“时分复用”了)。
什么,你还没有搞懂 “一个请求到来了,nginx使用epoll接收请求的过程是怎样的”, 多看看这个 图就了解了。提醒下,ngnix会有很多链接进来, epoll会把他们都监视起来,然后像拨开关一样, 谁有数据就拨向谁,然后调用相应的代码处理。
 了解这个基本的概念以后,其他的就很好解释了。
select, poll, epoll 都是I/O多路复用的具体的实现,之所以有这三个鬼存在,其实是他们出现是有 先后顺序的。
I/O多路复用这个概念被提出来以后, select是第一个实现 (1983 左右在BSD里面实现的)。
select 被实现以后,很快就暴露出了很多问题。
 select 会修改传入的参数数组,这个对于一个需要调用很多次的函数,是非常不友好的。 select 如果任何一个sock(I/O stream)出现了数据,select 仅仅会返回,但是并不会告诉你是那 个sock上有数据,于是你只能自己一个一个的找,10几个sock可能还好,要是几万的sock每次 select 不是线程安全的,如果你把一个sock加入到select, 然后突然另外一个线程发现,尼玛,这 个sock不用,要收回。对不起,这个select 不支持的,如果你丧心病狂的竟然关掉这个sock, select的标准行为是。。呃。。不可预测的, 这个可是写在文档中的哦.   “If a file descriptor being monitored by select() is closed in another thread, the result is unspecified.">
<meta property="og:url" content="https://sdttttt.github.io/blog/multiplexingio/">
<meta property="og:site_name" content="SDTTTTT">
<meta property="og:type" content="article"><meta property="article:section" content="Blog"><meta property="article:tag" content="unix"><meta property="article:published_time" content="2020-04-06T12:42:26+08:00"><meta property="article:modified_time" content="2020-09-24T18:06:32+08:00">
<meta name=twitter:title content="MultiplexingIO">
<meta name=twitter:description content="其实“I/O多路复用”这个坑爹翻译可能是这个概念在中文里面如此难理解的原因。所谓的I/O多路 复用在英文中其实叫 I/O multiplexing. 如果你搜索multiplexing啥意思,基本上都会出这个图:
于是大部分人都直接联想到&amp;quot;一根网线,多个sock复用&amp;quot; 这个概念,包括上面的几个回答, 其实不 管你用多进程还是I/O多路复用, 网线都只有一根好伐。多个Sock复用一根网线这个功能是在内核 +驱动层实现的.
重要的事情再说一遍: I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记 录跟踪每一个Sock(I/O流)的状态(对应空管塔里面的Fight progress strip槽)来同时管理多个I/O 流. 发明它的原因,是尽量多的提高服务器的吞吐能力。
是不是听起来好拗口,看个图就懂了.
在同一个线程里面, 通过拨开关的方式,来同时传输多个I/O流, (学过EE的人现在可以站出来义正 严辞说这个叫“时分复用”了)。
什么,你还没有搞懂 “一个请求到来了,nginx使用epoll接收请求的过程是怎样的”, 多看看这个 图就了解了。提醒下,ngnix会有很多链接进来, epoll会把他们都监视起来,然后像拨开关一样, 谁有数据就拨向谁,然后调用相应的代码处理。
 了解这个基本的概念以后,其他的就很好解释了。
select, poll, epoll 都是I/O多路复用的具体的实现,之所以有这三个鬼存在,其实是他们出现是有 先后顺序的。
I/O多路复用这个概念被提出来以后, select是第一个实现 (1983 左右在BSD里面实现的)。
select 被实现以后,很快就暴露出了很多问题。
 select 会修改传入的参数数组,这个对于一个需要调用很多次的函数,是非常不友好的。 select 如果任何一个sock(I/O stream)出现了数据,select 仅仅会返回,但是并不会告诉你是那 个sock上有数据,于是你只能自己一个一个的找,10几个sock可能还好,要是几万的sock每次 select 不是线程安全的,如果你把一个sock加入到select, 然后突然另外一个线程发现,尼玛,这 个sock不用,要收回。对不起,这个select 不支持的,如果你丧心病狂的竟然关掉这个sock, select的标准行为是。。呃。。不可预测的, 这个可是写在文档中的哦.   “If a file descriptor being monitored by select() is closed in another thread, the result is unspecified.">
</head><body class=article-page>
<script>(function(){const e="StackColorScheme";localStorage.getItem(e)||localStorage.setItem(e,"auto")})()</script><script>(function(){const t="StackColorScheme",e=localStorage.getItem(t),n=window.matchMedia("(prefers-color-scheme: dark)").matches===!0;e=="dark"||e==="auto"&&n?document.documentElement.dataset.scheme="dark":document.documentElement.dataset.scheme="light"})()</script>
<div class="container main-container flex on-phone--column compact"><aside class="sidebar left-sidebar sticky">
<button class="hamburger hamburger--spin" type=button id=toggle-menu aria-label="Toggle Menu">
<span class=hamburger-box>
<span class=hamburger-inner></span>
</span>
</button>
<header>
<figure class=site-avatar>
<a href=/>
<img src=/img/avatar_hu8e30117ca872857dd9f41f234a693048_441529_300x0_resize_box_3.png width=300 height=300 class=site-logo loading=lazy alt=Avatar>
</a>
</figure><div class=site-meta>
<h1 class=site-name><a href=/>SDTTTTT</a></h1><h2 class=site-description>臭鱼烂虾</h2></div></header><ol class=menu id=main-menu>
<div class=menu-bottom-section>
<li id=dark-mode-toggle><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-toggle-left" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><circle cx="8" cy="12" r="2"/><rect x="2" y="6" width="20" height="12" rx="6"/></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-toggle-right" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><circle cx="16" cy="12" r="2"/><rect x="2" y="6" width="20" height="12" rx="6"/></svg>
<span>Dark Mode</span>
</li></div></ol></aside><main class="main full-width">
<article class=main-article>
<header class=article-header>
<div class=article-details>
<div class=article-title-wrapper>
<h2 class=article-title>
<a href=/blog/multiplexingio/>MultiplexingIO</a>
</h2></div><footer class=article-time>
<div><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-calendar-time" width="56" height="56" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><path d="M11.795 21H5a2 2 0 01-2-2V7a2 2 0 012-2h12a2 2 0 012 2v4"/><circle cx="18" cy="18" r="4"/><path d="M15 3v4"/><path d="M7 3v4"/><path d="M3 11h16"/><path d="M18 16.496V18l1 1"/></svg>
<time class=article-time--published>Apr 06, 2020</time>
</div><div><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-clock" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><circle cx="12" cy="12" r="9"/><polyline points="12 7 12 12 15 15"/></svg>
<time class=article-time--reading>
1 minute read
</time>
</div></footer></div></header><section class=article-content>
<p>其实“I/O多路复用”这个坑爹翻译可能是这个概念在中文里面如此难理解的原因。所谓的I/O多路
复用在英文中其实叫 I/O multiplexing. 如果你搜索multiplexing啥意思,基本上都会出这个图:</p><p><img src=/multiplexIO/1.png loading=lazy></p><p>于是大部分人都直接联想到"一根网线,多个sock复用" 这个概念,包括上面的几个回答, 其实不
管你用多进程还是I/O多路复用, 网线都只有一根好伐。<strong>多个Sock复用一根网线这个功能是在内核
+驱动层实现的.</strong></p><p><strong>重要的事情再说一遍: I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记
录跟踪每一个Sock(I/O流)的状态(对应空管塔里面的Fight progress strip槽)来同时管理多个I/O
流.</strong> 发明它的原因,是尽量多的提高服务器的吞吐能力。</p><p>是不是听起来好拗口,看个图就懂了.</p><p><img src=/multiplexIO/2.png loading=lazy></p><p>在同一个线程里面, 通过拨开关的方式,来同时传输多个I/O流, (学过EE的人现在可以站出来义正
严辞说这个叫“时分复用”了)。</p><p>什么,你还没有搞懂 “一个请求到来了,nginx使用epoll接收请求的过程是怎样的”, 多看看这个
图就了解了。提醒下,ngnix会有很多链接进来, epoll会把他们都监视起来,然后像拨开关一样,
谁有数据就拨向谁,然后调用相应的代码处理。</p><hr>
<p>了解这个基本的概念以后,其他的就很好解释了。</p><p><strong>select, poll, epoll 都是I/O多路复用的具体的实现,之所以有这三个鬼存在,其实是他们出现是有
先后顺序的。</strong></p><p>I/O多路复用这个概念被提出来以后, select是第一个实现 (1983 左右在BSD里面实现的)。</p><p>select 被实现以后,很快就暴露出了很多问题。</p><ul>
<li>select 会修改传入的参数数组,这个对于一个需要调用很多次的函数,是非常不友好的。</li><li>select 如果任何一个sock(I/O stream)出现了数据,select 仅仅会返回,但是并不会告诉你是那
个sock上有数据,于是你只能自己一个一个的找,10几个sock可能还好,要是几万的sock每次</li><li>select 不是线程安全的,如果你把一个sock加入到select, 然后突然另外一个线程发现,尼玛,这
个sock不用,要收回。对不起,这个select 不支持的,如果你丧心病狂的竟然关掉这个sock,
select的标准行为是。。呃。。不可预测的, 这个可是写在文档中的哦.</li></ul><blockquote>
<p>“If a file descriptor being monitored by select() is closed in another thread, the result is
unspecified.”</p></blockquote><p>于是14年以后(1997年)一帮人又实现了poll, poll 修复了select的很多问题,比如:</p><ul>
<li>poll 去掉了1024个链接的限制,于是要多少链接呢, 主人你开心就好。</li><li>poll 从设计上来说,不再修改传入数组,不过这个要看你的平台了,所以行走江湖,还是小心为
妙.</li></ul><p><strong>其实拖14年那么久也不是效率问题, 而是那个时代的硬件实在太弱,一台服务器处理1千多个链接
简直就是神一样的存在了,select很长段时间已经满足需求。</strong></p><hr>
<p>于是5年以后, 在2002, 大神 Davide Libenzi 实现了epoll.</p><p>epoll 可以说是I/O 多路复用最新的一个实现,epoll 修复了poll 和select绝大部分问题, 比如:</p><ul>
<li>epoll 现在是线程安全的。</li><li>epoll 现在不仅告诉你sock组里面数据,还会告诉你具体哪个sock有数据,你不用自己去找了。</li></ul><p>可是epoll 有个致命的缺点。。只有linux支持。比如BSD上面对应的实现是kqueue。</p><p>其实有些国内知名厂商把epoll从安卓里面裁掉这种脑残的事情我会主动告诉你嘛。什么,你说没人
用安卓做服务器,尼玛你是看不起p2p软件了啦。</p><p>而ngnix 的设计原则里面, 它会使用目标平台上面最高效的I/O多路复用模型咯,所以才会有这个
设置。一般情况下,如果可能的话,尽量都用epoll/kqueue吧。</p></section><footer class=article-footer>
<section class=article-tags>
<a href=/tags/unix/>unix</a>
</section><section class=article-lastmod><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-clock" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><circle cx="12" cy="12" r="9"/><polyline points="12 7 12 12 15 15"/></svg>
<span>
Last updated on Sep 24, 2020 18:06 CST
</span>
</section></footer></article><div id=gitalk-container></div><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/gitalk@1.7.2/dist/gitalk.css>
<script src=https://cdn.jsdelivr.net/npm/gitalk@1.7.2/dist/gitalk.min.js></script>
<script src=https://cdn.jsdelivr.net/npm/blueimp-md5@2.18.0/js/md5.min.js></script>
<script>const gitalk=new Gitalk({clientID:"97eb9ce8ac126f0c7833",clientSecret:"5da440441b500b0b016928640712a1b1a03a5f8f",repo:"sdttttt/sdttttt.github.io",owner:"sdttttt",admin:["sdttttt"],distractionFreeMode:!1,id:md5(location.pathname)});(function(){if(["localhost","127.0.0.1"].indexOf(window.location.hostname)!=-1){document.getElementById("gitalk-container").innerHTML="Gitalk comments not available by default when the website is previewed locally.";return}gitalk.render("gitalk-container")})()</script>
<footer class=site-footer>
<section class=copyright>
&copy;
2022 SDTTTTT
</section><section class=powerby>
Built with <a href=https://gohugo.io/ target=_blank rel=noopener>Hugo</a> <br>
Theme <b><a href=https://github.com/CaiJimmy/hugo-theme-stack target=_blank rel=noopener data-version=3.10.0>Stack</a></b> designed by <a href=https://jimmycai.com target=_blank rel=noopener>Jimmy</a>
</section></footer><div class=pswp tabindex=-1 role=dialog aria-hidden=true>
<div class=pswp__bg></div><div class=pswp__scroll-wrap>
<div class=pswp__container>
<div class=pswp__item></div><div class=pswp__item></div><div class=pswp__item></div></div><div class="pswp__ui pswp__ui--hidden">
<div class=pswp__top-bar>
<div class=pswp__counter></div><button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
<button class="pswp__button pswp__button--share" title=Share></button>
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
<div class=pswp__preloader>
<div class=pswp__preloader__icn>
<div class=pswp__preloader__cut>
<div class=pswp__preloader__donut></div></div></div></div></div><div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class=pswp__share-tooltip></div></div><button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
</button>
<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
</button>
<div class=pswp__caption>
<div class=pswp__caption__center></div></div></div></div></div><script src=https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js integrity="sha256-ePwmChbbvXbsO02lbM3HoHbSHTHFAeChekF1xKJdleo=" crossorigin=anonymous defer></script><script src=https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js integrity="sha256-UKkzOn/w1mBxRmLLGrSeyB4e1xbrp4xylgAWb3M42pU=" crossorigin=anonymous defer></script><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.css integrity="sha256-c0uckgykQ9v5k+IqViZOZKc47Jn7KQil4/MP3ySA3F8=" crossorigin=anonymous><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.css integrity="sha256-SBLU4vv6CA6lHsZ1XyTdhyjJxCjPif/TRkjnsyGAGnE=" crossorigin=anonymous>
</main></div><script src=https://cdn.jsdelivr.net/npm/node-vibrant@3.1.5/dist/vibrant.min.js integrity="sha256-5NovOZc4iwiAWTYIFiIM7DxKUXKWvpVEuMEPLzcm5/g=" crossorigin=anonymous></script><script type=text/javascript src=/ts/main.js defer></script>
<script>(function(){const e=document.createElement("link");e.href="https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&display=swap",e.type="text/css",e.rel="stylesheet",document.head.appendChild(e)})()</script>
</body></html>